两种方式的点灯


摘要: "从根源上了解裸机编程是有好处的"


目录


前言

我实现学习的软件开发,再学习硬件开发的,之前从事后端开发的过程,大多是与高级语言(Java、Python、JS)以及各种设计模式、数据分析、架构关系等内容打交道。

在那个行业里,无数的工具在生态中极其丰富,完善到几乎不是在编程,借用一个 AI 科学家的话来说:

在2025年开发 Web 应用,有点像组装宜家家具。你必须拼凑和配置许多单独的服务:前端、后端、cdn、https、数据库、身份验证、付款......

你的工作不是写代码,而是配置、管道、编排、工作流、最佳实践。-- Andrej Karpath

人们慢慢进入到了 AI 编程的时代,一切都像组装积木一样拼拼凑凑,这也是我在业余时间打算学习硬件开发的原因:不要离开底层技术太久,那会忘记我们从何处开始的,迷失在了这个繁荣的,靠组装搬和运代码为生的时代。

我打算为 STM32F103C8T6 编写一整个系列的文章,记录我从零开始学习,直到掌握它,并且作出一个硬件成品的历程(这个成品大概会是一个手表)。

很多时候,我都难以逃脱本身作为软件开发者这个职业的视角来看待硬件技术,但我觉得这为我提供了更多的想法以及思路。


硬件

  1. STM32F103C8T6 开发板(Blue Pill)
  2. ST-LINK V2 或者 JLink

我打算以两种方式来点亮板载的 PC13 LED,一种使用操作寄存器的方式,另一种使用 HAL 库的方式,这两种方式几乎是两种极端,前者是最原始的方式(就像手写 Socket 连接发送 HTTP 请求),后者则是封装性和抽象性较高的方式(就像是直接使用线程的 HTTP 框架发送请求)。

在学习 STM32 的过程中,接触到了这几个库:CMSIS、SPL、LL、HAL。

CMSIS

CMSIS(Cortex Microcontroller Software Interface Standard)是 ARM 公司制定的一套嵌入式软件开发标准,旨在提供一致的编程接口和软件支持工具集,以简化 ARM Cortex 的软件开发,因为 STM32 使用的是 Arm 公司的 Cortex M3 芯片,所以这个库相当于是核心中的核心,用于控制和封装底层内核。

SPL

SPL(Standard Peripheral Library)是 ST 公司早些时候开发的框架,为了方便程序员不用再去记忆和操纵那丑陋的寄存器地址而诞生,但是现在 ST 公司已经决定停止开发这款软件,新版本的芯片不再支持它,但大部分的教程还在用这个库,还有很多很多的过去的历史项目都离不开它(就像后端开发里的 Java 8 + SpringBoot 2.x + CentOS7.x 组合哈哈哈)。

LL

LL(Low-layer)ST 开发的框架,提供比 HAL 更接近底层的 API,执行效率更高,控制更加精细,适用于对性能要求较高的场景,例如实时控制、低功耗应用。

HAL

HAL(Hardware Abstraction Layer)ST 官方推荐的主流开发库,适用于 STM32 所有系列,具有更高的抽象层次,提供较为通用的 API,可移植性强,配合 STM32CubeMX 生成的代码,便于图形化配置。

按照我的个人理解,CMSIS 是最核心的,它是 ARM 公司为了 Cortex 内核开发的库,ST 公司开发的 LL、HAL、SPL 都会间接依赖于这个库。

而 SPL 被官方 Deprecated 了,官方主推 HAL 库,但是配置各种初始化的外设功能依然麻烦,每次初始化工程,都要手敲很麻烦,所以 ST 公司搞了个 STM32CubeMX 的可视化工具,通过点点鼠标来生成初始化代码(很熟悉,就像 Spring 朝着 Spring Boot 发展,手写 SQL 慢慢被可视化的各种 Code Generator 所取代)。

学到这里,发现软件硬件都是差不多的,核心的思想都是分层架构:最底层代表了最原始、最直接、功能最多、权限最大,但同时也是复杂度最高,最难记忆和理解,底层更贴近机器,但对人类不友好。而上层通过抽象和封装,牺牲了机器执行效率以换取人类的开发效率,远离机器的同时,通过可视化和代码生成,更加人性化。


Bare Metal

在这里,只有一台装着最基本的代码编辑器,代码编译器的电脑,一个连接着电脑的 Blue Pill,一些关于 Cortex M3 和 STM32 的技术手册,以及一个绝望的你。

没有花里胡哨的 IDE、没有代码生成工具,没有互联网的示例供你直接复制粘贴运行,你必须直面裸金属,直面混乱丑陋的十六进制的寄存器地址。

在实践里,很少有人会直接使用寄存器编程,虽然这是它的本质(目前,我是这么理解的):通过配置和操作寄存器来控制外设(如 GPIO、UART、SPI、I2C、定时器、ADC 等),所有的操作,本质上都是对寄存器进行修改。

按照过去软件的学习经验来说,譬如 Python,肯定是先学习它的语法,编写 py 文件,了解虚拟环境,了解多个文件如何处理依赖关系,模块、包的种种概念,了然于胸之后,再去使用 Pycharm 之类的高级 IDE 编写工程代码,。

所以,就算以后要用 Keli、STM32CubeIDE 之类的高级工具,但依然要先从一个最简单的代码编辑器 + 代码编译器开始。

这里选用的是 Vim + arm-none-eabi 工具链。

上电启动过程

由于没有一个完整的操作系统来帮我们启动硬件,不得不去仔细了解下 STM32 上电后的启动过程。

这个启动过程不是说上电以后,直接就跳转到我们写的 main 函数里面运行,而是包含了很多步骤,需要了解以下概念:

  1. 内存模型和内存映射
  2. 启动文件和链接文件
  3. 中断向量表